package com.flynn.rock.crm.service.customer.impl;

import com.flynn.rock.crm.convert.AbstractCustomerConvert;
import com.flynn.rock.crm.entity.customer.CustomerEntity;
import com.flynn.rock.crm.entity.customer.CustomerFollowUpRecordEntity;
import com.flynn.rock.crm.entity.customer.CustomerOperateRecordEntity;
import com.flynn.rock.crm.entity.employee.EmployeeEntity;
import com.flynn.rock.crm.enums.CustomerQueryType;
import com.flynn.rock.crm.model.customer.QueryCustomerCriteria;
import com.flynn.rock.crm.model.customer.request.PageCustomerRequest;
import com.flynn.rock.crm.model.customer.request.PageFollowUpRecordRequest;
import com.flynn.rock.crm.model.customer.request.PageOperateRecordRequest;
import com.flynn.rock.crm.model.customer.request.SaveCustomerRequest;
import com.flynn.rock.crm.model.customer.request.SaveFollowUpRecordRequest;
import com.flynn.rock.crm.model.customer.request.TransferCustomerRequest;
import com.flynn.rock.crm.model.customer.vo.CustomerIdVo;
import com.flynn.rock.crm.model.customer.vo.CustomerVo;
import com.flynn.rock.crm.model.customer.vo.FollowUpRecordIdVo;
import com.flynn.rock.crm.model.customer.vo.ListCustomerVo;
import com.flynn.rock.crm.model.customer.vo.ListFollowUpRecordVo;
import com.flynn.rock.crm.model.customer.vo.ListOperateRecordVo;
import com.flynn.rock.crm.repository.customer.CustomerFollowUpRecordRepository;
import com.flynn.rock.crm.repository.customer.CustomerOperateRecordRepository;
import com.flynn.rock.crm.repository.customer.CustomerOrderRepository;
import com.flynn.rock.crm.repository.customer.CustomerRepository;
import com.flynn.rock.crm.repository.employee.EmployeeRepository;
import com.flynn.rock.crm.service.customer.CustomerService;
import com.flynn.rock.enums.customer.CustomerOrderStatus;
import com.flynn.rock.enums.system.NoIdentifierType;
import com.flynn.rock.model.PageResult;
import com.flynn.rock.model.dto.CustomerOperateRecordDto;
import com.flynn.rock.model.dto.system.UserAccountDto;
import com.flynn.rock.system.api.ISysMessageApi;
import com.flynn.rock.system.api.ISysNoIdentifierApi;
import com.flynn.rock.system.model.dto.SaveMessageDto;
import com.flynn.rock.util.JpaQueryHelper;
import com.flynn.rock.util.PageUtil;
import com.flynn.rock.util.SecurityUtil;
import jakarta.annotation.Resource;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Service
public class CustomerServiceImpl implements CustomerService {

    @Resource
    private CustomerRepository customerRepository;

    @Resource
    private EmployeeRepository employeeRepository;

    @Resource
    private CustomerOperateRecordRepository customerOperateRecordRepository;

    @Resource
    private CustomerFollowUpRecordRepository customerFollowUpRecordRepository;

    @Resource
    private CustomerOrderRepository customerOrderRepository;

    @Resource
    private ISysNoIdentifierApi sysNoIdentifierApi;

    @Resource
    private ISysMessageApi sysMessageApi;

    @Override
    public CustomerIdVo saveCustomer(SaveCustomerRequest request) {
        String operateTitle;
        String operateContent;
        LocalDateTime now = LocalDateTime.now();
        UserAccountDto currentUser = SecurityUtil.getCurrentUser();
        CustomerEntity newEntity = AbstractCustomerConvert.INSTANCE.toEntity(request);
        if (Objects.nonNull(request.getCustomerId())) {
            CustomerEntity oldEntity = customerRepository.findById(request.getCustomerId()).orElseThrow();
            this.fillNewCustomerEntity(newEntity, oldEntity);
            operateTitle = "修改";
            operateContent = "客户 由 " + currentUser.getUser().getRealName() + " 修改";
        } else {
            newEntity.setCustomerCode(sysNoIdentifierApi.getNoIdentifier(NoIdentifierType.CUSTOMER_CODE));
            newEntity.setCreateUserId(currentUser.getUserId());
            if (Objects.nonNull(request.getFollowUpUserId())) {
                EmployeeEntity entity = employeeRepository.findByUserId(request.getFollowUpUserId());
                newEntity.setFollowUpUserName(entity.getRealName());
                newEntity.setLastFollowUpTime(LocalDateTime.now());
            }
            newEntity.setCreateTime(now);
            operateTitle = "创建";
            operateContent = "客户 由 " + currentUser.getUser().getRealName() + " 创建";
        }
        newEntity.setLastFollowUpTime(now);
        newEntity.setUpdateTime(now);
        newEntity = customerRepository.save(newEntity);
        CustomerOperateRecordDto operateRecordDto = CustomerOperateRecordDto.builder()
                .customerId(newEntity.getCustomerId())
                .operateUserId(currentUser.getUserId())
                .operateUserName(currentUser.getUser().getRealName())
                .operateTitle(operateTitle)
                .operateContent(operateContent)
                .build();
        customerOperateRecordRepository.saveCustomerOperateRecord(operateRecordDto);
        return CustomerIdVo.builder()
                .customerId(newEntity.getCustomerId())
                .customerCode(newEntity.getCustomerCode())
                .build();
    }

    private void fillNewCustomerEntity(CustomerEntity newEntity, CustomerEntity oldEntity) {
        newEntity.setCustomerCode(oldEntity.getCustomerCode());
        newEntity.setCreateTime(oldEntity.getCreateTime());
        newEntity.setCreateUserId(oldEntity.getCreateUserId());
        newEntity.setLastFollowUpTime(oldEntity.getLastFollowUpTime());
        newEntity.setFollowUpStatus(oldEntity.getFollowUpStatus());
        newEntity.setFollowUpUserId(oldEntity.getFollowUpUserId());
        newEntity.setFollowUpUserName(oldEntity.getFollowUpUserName());
    }

    @Override
    public CustomerVo getCustomer(Long customerId) {
        CustomerEntity customerEntity = customerRepository.findById(customerId).orElseThrow();
        return AbstractCustomerConvert.INSTANCE.toDetailVo(customerEntity);
    }

    @Override
    public void deleteCustomer(Long customerId) {
        CustomerEntity customerEntity = customerRepository.findById(customerId).orElseThrow();
        customerRepository.delete(customerEntity);
    }

    @Override
    public PageResult<ListCustomerVo> pageCustomer(PageCustomerRequest request) {
        QueryCustomerCriteria criteria = QueryCustomerCriteria.builder()
                .customerName(request.getCustomerName())
                .identityNumber(request.getIdentityNumber())
                .customerCode(request.getCustomerCode())
                .phone(request.getPhone())
                .build();
        LocalDateTime currDateTime = LocalDateTime.now();
        PageRequest pageRequest = PageRequest.of(request.getPageNum() - 1, request.getPageSize());
        pageRequest = pageRequest.withSort(Sort.sort(CustomerEntity.class).by(CustomerEntity::getUpdateTime).descending());
        Page<CustomerEntity> page = customerRepository.findAll(
                ((root, query, criteriaBuilder) -> {
                    List<Predicate> predicates = new ArrayList<>();
                    predicates.add(JpaQueryHelper.getPredicate(root, criteria, criteriaBuilder));
                    Path<Object> status = root.get("status");
                    Path<Object> customerRank = root.get("customerRank");
                    Path<Object> followUpUserId = root.get("followUpUserId");
                    if (Objects.nonNull(request.getStatus())) {
                        predicates.add(criteriaBuilder.equal(status, request.getStatus()));
                    }
                    if (Objects.nonNull(request.getCustomerRank())) {
                        predicates.add(criteriaBuilder.equal(customerRank, request.getCustomerRank()));
                    }
                    if (CustomerQueryType.NOT_FOLLOW_UP.equals(request.getQueryType())) {
                        predicates.add(criteriaBuilder.isNull(followUpUserId));
                    } else {
                        predicates.add(criteriaBuilder.equal(followUpUserId, SecurityUtil.getCurrentUserId()));
                    }
                    if (Objects.nonNull(request.getNoFollowUpMinDay())) {
                        LocalDateTime localDateTime = currDateTime.minusDays(request.getNoFollowUpMinDay());
                        predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("lastFollowUpTime"), localDateTime));
                    }
                    if (Objects.nonNull(request.getNoFollowUpMaxDay())) {
                        LocalDateTime localDateTime = currDateTime.minusDays(request.getNoFollowUpMaxDay());
                        predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("lastFollowUpTime"), localDateTime));
                    }
                    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
                }),
                pageRequest
        );
        return PageUtil.toPage(page.map(AbstractCustomerConvert.INSTANCE::toListCustomerVo));
    }

    @Transactional
    @Override
    public void transferCustomer(TransferCustomerRequest request) {
        CustomerEntity customerEntity = customerRepository.findById(request.getCustomerId()).orElseThrow();
        UserAccountDto currentUser = SecurityUtil.getCurrentUser();
        CustomerOperateRecordDto operateRecordDto = CustomerOperateRecordDto.builder()
                .customerId(customerEntity.getCustomerId())
                .operateUserId(currentUser.getUserId())
                .operateUserName(currentUser.getUser().getRealName())
                .operateTitle("转派")
                .build();
        if (Objects.nonNull(request.getFollowUpUserId())) {
            customerEntity.setFollowUpUserId(request.getFollowUpUserId());
            EmployeeEntity entity = employeeRepository.findByUserId(request.getFollowUpUserId());
            customerEntity.setFollowUpUserName(entity.getRealName());
            operateRecordDto.setOperateContent("客户 由 " + currentUser.getUser().getRealName() + " 转派给 " + entity.getRealName());
        } else {
            // 放入公海
            if (customerOrderRepository.existsByCustomerIdAndOrderStatusIn(request.getCustomerId(),
                    CustomerOrderStatus.NOT_END_STATUS)) {
                throw new RuntimeException("当前客户存在正在生效的邀约，不能放入公海");
            }
            customerEntity.setFollowUpUserId(null);
            customerEntity.setFollowUpUserName(null);
            operateRecordDto.setOperateContent("客户 由 " + currentUser.getUser().getRealName() + "放入公海");
        }
        customerRepository.save(customerEntity);
        customerOperateRecordRepository.saveCustomerOperateRecord(operateRecordDto);
        customerRepository.updateLastFollowUpTime(customerEntity.getCustomerId(), LocalDateTime.now());
        if (Objects.nonNull(request.getFollowUpUserId())) {
            SaveMessageDto messageDto = getSaveMessageDto(request, currentUser, customerEntity);
            sysMessageApi.saveMessage(messageDto);
        }
    }

    private static SaveMessageDto getSaveMessageDto(TransferCustomerRequest request, UserAccountDto currentUser,
                                                    CustomerEntity customerEntity) {
        SaveMessageDto messageDto = new SaveMessageDto();
        messageDto.setFromUserId(currentUser.getUser().getUserId());
        messageDto.setFromUsername(currentUser.getUser().getRealName());
        messageDto.setToUserId(request.getFollowUpUserId());
        messageDto.setMessageTitle("客户转派通知");
        messageDto.setMessageContent("编号为" + customerEntity.getCustomerCode() + "的" +
                customerEntity.getCustomerName() + "客户由 " + currentUser.getUser().getRealName() + " 转派给您");
        messageDto.setMessageBusinessType("CUSTOMER");
        return messageDto;
    }

    @Transactional
    @Override
    public FollowUpRecordIdVo saveFollowUpRecord(SaveFollowUpRecordRequest request) {
        UserAccountDto currentUser = SecurityUtil.getCurrentUser();
        CustomerFollowUpRecordEntity followUpRecode = AbstractCustomerConvert.INSTANCE.toFollowUpRecodeEntity(request);
        followUpRecode.setFollowUpUserId(currentUser.getUserId());
        followUpRecode.setFollowUpUserName(currentUser.getUser().getRealName());
        followUpRecode = customerFollowUpRecordRepository.save(followUpRecode);
        customerRepository.updateLastFollowUpTime(followUpRecode.getCustomerId(), LocalDateTime.now());
        return FollowUpRecordIdVo.builder()
                .id(followUpRecode.getId())
                .build();
    }

    @Override
    public PageResult<ListFollowUpRecordVo> pageFollowUpRecord(PageFollowUpRecordRequest request) {
        PageRequest pageRequest = PageRequest.of(request.getPageNum() - 1, request.getPageSize());
        pageRequest = pageRequest.withSort(Sort.sort(CustomerFollowUpRecordEntity.class).by(CustomerFollowUpRecordEntity::getUpdateTime).descending());
        Page<CustomerFollowUpRecordEntity> entityPage = customerFollowUpRecordRepository.findAll(
                ((root, query, criteriaBuilder) -> {
                    List<Predicate> predicates = new ArrayList<>();
                    Path<Object> customerId = root.get("customerId");
                    predicates.add(criteriaBuilder.equal(customerId, request.getCustomerId()));
                    if (StringUtils.hasText(request.getFollowUpContent())) {
                        predicates.add(criteriaBuilder.like(root.get("followUpContent"), "%" + request.getFollowUpContent() + "%"));
                    }
                    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
                }),
                pageRequest
        );
        return PageUtil.toPage(entityPage.map(AbstractCustomerConvert.INSTANCE::toListFollowUpRecordVo));
    }

    @Override
    public PageResult<ListOperateRecordVo> pageOperateRecord(PageOperateRecordRequest request) {
        PageRequest pageRequest = PageRequest.of(request.getPageNum() - 1, request.getPageSize());
        pageRequest = pageRequest.withSort(Sort.sort(CustomerOperateRecordEntity.class).by(CustomerOperateRecordEntity::getUpdateTime).descending());
        Page<CustomerOperateRecordEntity> entityPage = customerOperateRecordRepository.findAll(
                ((root, query, criteriaBuilder) -> {
                    List<Predicate> predicates = new ArrayList<>();
                    Path<Object> customerId = root.get("customerId");
                    predicates.add(criteriaBuilder.equal(customerId, request.getCustomerId()));
                    if (StringUtils.hasText(request.getOperateContent())) {
                        predicates.add(criteriaBuilder.like(root.get("operateContent"), "%" + request.getOperateContent() + "%"));

                    }
                    return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
                }),
                pageRequest
        );
        return PageUtil.toPage(entityPage.map(AbstractCustomerConvert.INSTANCE::toListOperateRecordVo));
    }
}
